package dyyx.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import dyyx.dto.SystemMetrics;

public abstract class ProcUtil {
	
	private static final String SEP = " ";
	
	private static final char TAB = '\t';
	private static final char SPACE = ' ';
	//  colon
	private static final String  COLON = ":";
	
	
	private static final Set<String> whiteList = new HashSet<String>();
	
	private static final Set<String> processStatusWhiteList = new HashSet<String>();

	
	// can not use set here !!!
	private static final List<String> diskStatColumns = new ArrayList<String>();
	
	// 累计值 
	// 
	private static final Set<String> totalColumns = new HashSet<String>();
	
	static{
		// TODO config
		whiteList.add("MemTotal");
		whiteList.add("MemFree");
		whiteList.add("MemAvailable");
		whiteList.add("Buffers");
		whiteList.add("Cached");
		whiteList.add("SwapCached");
		
		whiteList.add("SwapTotal");
		whiteList.add("SwapFree");
		
		// 
		diskStatColumns.add("id");
		diskStatColumns.add("id2");
		diskStatColumns.add("name");
		
		diskStatColumns.add("totalRead");
		diskStatColumns.add("totalMergeRead");
		diskStatColumns.add("readSectors");
		diskStatColumns.add("totalReadTime");
		
		diskStatColumns.add("totalWrite");
		diskStatColumns.add("totalMergeWrite");
		diskStatColumns.add("writeSectors");
		diskStatColumns.add("totalWriteTime");

		diskStatColumns.add("requests");
		diskStatColumns.add("totalTime");
		diskStatColumns.add("totalWeightTime");
		
		// total 
		totalColumns.add("totalRead");
		totalColumns.add("totalMergeRead");
		totalColumns.add("readSectors");
		totalColumns.add("totalReadTime");
		
		totalColumns.add("totalWrite");
		totalColumns.add("totalMergeWrite");
		totalColumns.add("writeSectors");
		totalColumns.add("totalWriteTime");
		totalColumns.add("totalTime");
		totalColumns.add("totalWeightTime");
		
		totalColumns.add("netRecvBytes");
		totalColumns.add("netSendBytes");
		
		// processStatusWhiteList
		processStatusWhiteList.add("VmData");
		processStatusWhiteList.add("VmLib");
		processStatusWhiteList.add("VmHWM");
		processStatusWhiteList.add("VmLck");
		processStatusWhiteList.add("VmRSS");
		processStatusWhiteList.add("VmExe");
		processStatusWhiteList.add("nonvoluntary_ctxt_switches");
		processStatusWhiteList.add("voluntary_ctxt_switches");
		processStatusWhiteList.add("VmPeak");
		processStatusWhiteList.add("RssFile");
		processStatusWhiteList.add("VmStk");


		

	}
	
	public static Map<String,String> getProcessStatusMapSimple(long pid){
		return getProcessStatusMap(pid,processStatusWhiteList);
	}

	public static Map<String,String> getProcessStatusMap(long pid){
		return getProcessStatusMap(pid,null);
	}

	
	public static Map<String,String> getProcessStatusMap(long pid,Set<String> whiteList){
		if(pid<=0){
			return null;
		}
		
		Map<String,String> data = new HashMap<String,String>();
		
		
		try{
			String file = "/proc/"+pid+"/status";
			List<String> list = CommUtil.readLines(file, null, true);
			
			if(list==null || list.isEmpty()){
	    		return data;
	    	}
	    	
	    	for(String item:list){
	    		buildInfoSingleLine(data,item,whiteList);
	    	}
	    	
		}catch(Throwable e){
			// sm.errorInfo.put("buildMemInfoError", e+"");
			
			return data;
		}
		
		return data;
	}
	
	
	
	
	
	public static SystemMetrics buildMetricData(){
		
		String file,str;
		SystemMetrics sm = new SystemMetrics();
		
		Map<String,String> map = new HashMap<String,String>();
		
		long start = System.currentTimeMillis();
		List<String> list = null;
		try{
			file = "/proc/loadavg";
			list = CommUtil.readLines(file, null, true);
			buildLoadAvg(map,list);
		}catch(Throwable e){
			sm.errorInfo.put("buildLoadAvgError", e+"");
		}
		
		try{
			file = "/proc/meminfo";
			list = CommUtil.readLines(file, null, true);
			buildMemInfo(map,list);
		}catch(Throwable e){
			sm.errorInfo.put("buildMemInfoError", e+"");
		}
		
		try{
			file = "/proc/net/dev";
			list = CommUtil.readLines(file, null, true);
			buildNetDev(map,list);
		}catch(Throwable e){
			sm.errorInfo.put("buildNetDevError", e+"");
		}
		
		try{
			file = "/proc/diskstats";
			list = CommUtil.readLines(file, null, true);
			buildDiskStats(map,list);
		}catch(Throwable e){
			sm.errorInfo.put("buildDiskStatsError", e+"");
		}
		
		
		try{
			file = "/proc/sys/fs/file-nr";
			list = CommUtil.readLines(file, null, true);
			buildFileNr(map,list);
		}catch(Throwable e){
			sm.errorInfo.put("buildFileNrError", e+"");
		}
		
		
		long end = System.currentTimeMillis();
		long time = end - start;
		sm.time = time;
		sm.timestamp = end;
		sm.data = map;
		
		return sm;

	}
	
	

	public static Map<String,String> buildResult(SystemMetrics oldData,SystemMetrics newData){
		long oldTimestamp = oldData.timestamp;
		long newTimestamp = newData.timestamp;
		if(oldTimestamp<=0 || newTimestamp<=0){
			return null;
		}
		//
		long time = newTimestamp - oldTimestamp;
		Map<String,String> map = buildResult(oldData.data,newData.data,time);
		if(map!=null){
			map.put("collectTime", String.valueOf(newData.time));
		}
		return map;
	}

	
	
	/**
	 * 构建结果  累计字段 两次采样差值 / 采样时间间隔
	 * @param oldData 旧的指标数据
	 * @param newData 新的指标数据
	 * @param time    采样间隔时间 毫秒
	 * @return
	 */
	public static Map<String,String> buildResult(Map<String,String> oldData,Map<String,String> newData,long time){
		if(newData==null || newData.isEmpty()){
			return null;
		}
		if(time<=0){
			return null;
		}
		Map<String,String> map = new HashMap<String,String>();
		
		// copy not total column data from new data
		
		Set<String> keys = newData.keySet();
		for(String key:keys){
			if(totalColumns.contains(key)){
				continue;
			}
			map.put(key, newData.get(key));
		}
		
		long value = 0;
		for(String key:keys){
			if(!totalColumns.contains(key)){
				continue;
			}
			//
			value = buildResultForOneTotalColumn(oldData,newData,key,time);
			if(value<=0){
				continue;
			}
			map.put(key, value+"");
		}

		
		return map;	
	}
	
	private static long buildResultForOneTotalColumn(Map<String,String> oldData,Map<String,String> newData,String name,long time){
		String newstr = newData.get(name);
		if(CommUtil.isBlank(newstr)){
			return 0;
		}
		long newvalue = CommUtil.getLong(newstr);
		if(newvalue<=0){
			return 0;
		}
		
		String oldstr = oldData.get(name);
		if(CommUtil.isBlank(oldstr)){
			return 0;
		}
			
		long oldvalue = CommUtil.getLong(oldstr);
		if(oldvalue<=0){
			return 0;
		}
		if(oldvalue >= newvalue){
			return 0;
		}
		long dif = newvalue - oldvalue;
		
		dif = dif * 1000 / time;
		
		return dif;

	}
	
	

	
	
	public static void buildLoadAvg(Map<String,String> map,List<String> list){
		// proc/loadavg 
		// 2.46 2.59 2.68 2/3301 254766
		// 1min 5min 15min
		if(list==null || list.size()!=1){
			return;
		}
		String str = list.get(0);
		list = parse(str);
		
		if(list==null || list.size()<3){
			return;
		}
		
		map.put("load1", list.get(0));
		map.put("load5", list.get(1));
		map.put("load15", list.get(2));
		
	}
	
	public static void buildFileNr(Map<String,String> map,List<String> list){
		// /proc/sys/fs/file-nr
		// 5696	0	65536
		// TODO 只能读到第一个字节 ！！！
		// System.out.println("buildFileNr.str="+str);
		if(list==null || list.size()!=1){
			return;
		}
		String str = list.get(0);
		list = parse(str);
		// System.out.println("buildFileNr.list.size="+list.size());

		if(list==null || list.size()<3){
			return;
		}
		map.put("fileNrUse", list.get(0));
		map.put("fileNrMax", list.get(2));
		
	}
	
    public static void buildMemInfo(Map<String,String> map,List<String> list){
        	
    	if(list==null || list.isEmpty()){
    		return;
    	}
    	
    	for(String item:list){
    		buildInfoSingleLine(map,item,whiteList);
    	}
    	
		
	}
    
    public static void buildNetDev(Map<String,String> map,List<String> list){
    	if(list==null || list.isEmpty()){
    		return;
    	}
    	String firstIntfInfo = getFirstNetIntfInfo(list);
    	buildNetDevOne(map,firstIntfInfo);

	}
    
    
    public static void buildNetDevOne(Map<String,String> map,String str){
        List<String> list = parse(str);
    	
    	if(list==null || list.size()<15){
    		return;
    	}
    	
    	String netRecvBytes = list.get(1);
    	String netSendBytes = list.get(9);
    	
    	map.put("netRecvBytes", netRecvBytes);
    	map.put("netSendBytes", netSendBytes);
    }
    
    
    public static Map<String,String> parseProcDiskStat(String str){
    	if(CommUtil.isBlank(str)){
    		return null;
    	}
    	//  11 or 14 columns
    	List<String> list = parse(str);
    	if(list==null){
    		return null;
    	}
    	int num = list.size();
    	if(num!=11 && num!=14){
    		return null;
    	}
    	
    	// ProcDiskStat stat = new ProcDiskStat();
    	Map<String,String> map = new HashMap<String,String>();
    	
    	String name = null;
    	String value = null;
    	int nameIndexOffset = 0;
    	if(num==11){
    		nameIndexOffset = 3;
    	}
    	for(int i=0;i<num;i++){
    		value = list.get(i);
    		name = diskStatColumns.get(i+nameIndexOffset);
    		map.put(name, value);
    	}
    	
    	return map;
    }
    
    public static void buildDiskStats(Map<String,String> map,List<String> list){
    	
    	if(list==null || list.isEmpty()){
    		return;
    	}
    	Map<String,String> item;
    	
    	List<Map<String,String>> dataList = new ArrayList<Map<String,String>>();
    	for(String line:list){
    		item = parseProcDiskStat(line);
    		if(item==null || item.size()<11){
    			continue;
    		}
    		dataList.add(item);
    	}
    	
    	Map<String,String> diskStatMap = diskStatsSum(dataList);
    	
    	map.putAll(diskStatMap);
    	
    	
	}
    
    
    private static Map<String,String> diskStatsSum(List<Map<String,String>> list){
    	if(list==null || list.isEmpty()){
    		return  null;
    	}
    	Map<String,String> map = new HashMap<String,String>();
    	int num = diskStatColumns.size();
    	long value = 0;
    	String name = null;
    	
    	for(int i=3;i<num;i++){
    		name = diskStatColumns.get(i);
    		value = diskStatsSum(list,name);
    		map.put(name, value+"");
    	}
    	
    	return map;
    	
    }
    
    private static long diskStatsSum(List<Map<String,String>> list,String name){
    	long sum = 0;
    	String value = null;
    	long valuelong = 0;
    	for(Map<String,String> item:list){
    		value = item.get(name);
    		valuelong = CommUtil.getLong(value);
    		if(valuelong<=0){
    			continue;
    		}
    		sum = sum + valuelong;
    	}
    	return sum;
    }

    
    
    
    private static String getFirstNetIntfInfo(List<String> list){
    	for(String item:list){
    		if(item.indexOf("lo:")>=0){
    			continue;
    		}
    		if(item.indexOf(COLON)>0){
    			return item;
    		}
    	}
    	
    	return null;
    }
    
    
    private static void buildInfoSingleLine(Map<String,String> map,String line,Set<String> whiteList){
    	// MemTotal:       263536708 kB
    	// MemFree:        193868308 kB
    	// MemAvailable:   197602952 kB
    	
    	List<String> list = parse(line);
    	if(list==null || list.size() <2){
    		return;
    	}
    	
    	String name = list.get(0);
    	String value = list.get(1);
    	
    	name = getFinalName(name);
    	if(CommUtil.isBlank(name) || CommUtil.isBlank(value)){
    		return;
    	}
    	if(whiteList==null){
    		map.put(name, value);
    	}else{
    		if(whiteList.contains(name)){
        		map.put(name, value);
    		}
    	}
    	
    }
    
    
    public static String getFinalName(String name){
    	if(name==null){
    		return null;
    	}
    	if(name.endsWith(COLON)){
    		name = name.substring(0, name.length()-1);
    	}
    	
    	return name;	
    }
    
    
	
    public static List<String> parse(String str){
    	if(str==null){
    		return null;
    	}
    	if(str.indexOf(TAB)>=0){
    		str = CommUtil.replaceAll(str, TAB, SPACE);
    	}
    	
    	return CommUtil.split(str, SEP, true);
    }
	
	

}